package org.mentalsmash.tazio.evaluation.tests;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.mentalsmash.tazio.evaluation.mock.MediumCounter;
import org.mentalsmash.tazio.impl.WRMTazioStarter;
import org.mentalsmash.tazio.transactions.RollbackException;
import org.mentalsmash.tazio.transactions.Share;
import org.mentalsmash.tazio.transactions.Transaction;
import org.mentalsmash.tazio.transactions.TransactionalSystemException;
import org.mentalsmash.tazio.transactions.UserTransactionManager;
import org.mentalsmash.tazio.transactions.management.TransactionStateException;

public class MediumCounterTest {

    public static final int MAX_COUNTER = 200;
    private static final int sleepTime = 500;

    private Object _lock = new Object();
    private int threadFinished = 0;

    public static void main(String[] args) {
	String conf = args[0];
	int numThreads = Integer.parseInt(args[1]);
	MediumCounterTest instance = new MediumCounterTest();
	instance.doJob(conf, numThreads, sleepTime);
    }

    public void doJob(String conf, int numThreads, int sleeptime) {

	System.out.println("Starting Tazio...");
	WRMTazioStarter starter = new WRMTazioStarter(false, conf);
	starter.initTazio();
	System.out.println("Tazio started.");

	TestData mbean = new TestData();
	mbean.register();
	System.out.println("TestDataMBean registered.");

	Share share = starter.getShare();
	UserTransactionManager txManager = starter.getTransactionManager();

	MediumCounter testcounter = retrieveMediumCounter(share);
	
	try {
	    System.out.println("Counter size is : "
		    + testcounter.getString().getBytes().length + " bytes");
	} catch (Exception ex) {
	    ex.printStackTrace();
	    //System.exit(1);
	}

	MediumCounterSetter setter = new MediumCounterSetter(txManager,
		testcounter, MAX_COUNTER, sleeptime);

	List<Thread> threads = new ArrayList<Thread>(numThreads);

	for (int i = 0; i < numThreads; i++) {
	    Thread t = new Thread(setter);
	    t.setName("t" + i);
	    threads.add(t);

	}

	System.out.println("Starting test...");
	long now = System.currentTimeMillis();
	for (Thread t : threads) {
	    t.start();
	}

	synchronized (_lock) {
	    while (threadFinished < numThreads) {
		try {
		    _lock.wait();
		} catch (InterruptedException e) {
		    e.printStackTrace();
		}
	    }
	}

	long duration = System.currentTimeMillis() - now;
	mbean.setDuration(duration);
	mbean.setFinished();
	System.out.println("Test ended (" + duration + " ms).");

    }

    public MediumCounter retrieveMediumCounter(Share share) {
	String properties = "name:test-counter";
	MediumCounter testcounter = share.getObject(properties);

	if (testcounter == null) {
	    try {
		testcounter = share.createObject(MediumCounter.class,
			properties);
	    } catch (TransactionStateException e1) {
		e1.printStackTrace();
		throw new RuntimeException("Cannot retrieve counter object!");
	    } catch (RollbackException e1) {
		e1.printStackTrace();
		throw new RuntimeException("Cannot retrieve counter object!");
	    }
	} else {
	    System.out.println("Object retrieved from the share");
	}
	return testcounter;
    }

    class MediumCounterSetter implements Runnable {

	private final UserTransactionManager txm;

	private final MediumCounter toIncrement;

	private final int limit;

	private final ThreadLocal<String> name = new ThreadLocal<String>();
	private final int sleepTime;

	public MediumCounterSetter(UserTransactionManager txm,
		MediumCounter toIncrement, int limiut, int sleeptime) {
	    this.txm = txm;
	    this.toIncrement = toIncrement;
	    this.limit = limiut;
	    this.sleepTime = sleeptime;
	}

	public void run() {
	    this.name.set(Thread.currentThread().getName());
	    final Random rand = new Random();
	    for (;;) {
		try {
		    Transaction currentTx = txm.begin();

		    String payload = toIncrement.getString();
		    toIncrement.setString(payload);

		    int currentVal = toIncrement.getValue();
		    if (currentVal < this.limit) {
			toIncrement.setValue(currentVal + 1);
			currentTx.commit();
			System.out.println("<" + this.name.get()
				+ "> MediumCounter set to " + (currentVal + 1));
		    } else {
			System.out.println("<" + this.name.get()
				+ "> MediumCounter to its max value");
			currentTx.rollback();
			break;
		    }

		} catch (RollbackException e) {
		    System.err.println("<" + this.name.get()
			    + "> a transaction rolled back!");
		} catch (TransactionStateException e) {
		    e.printStackTrace();
		} catch (TransactionalSystemException e) {
		    e.printStackTrace();
		}
		try {
		    Thread.sleep(rand.nextInt(this.sleepTime));
		} catch (InterruptedException e) {
		    e.printStackTrace();
		}

	    }

	    synchronized (_lock) {
		threadFinished++;
		_lock.notifyAll();
	    }

	}
    }

}
